package com.apache.rpc;

import com.apache.api.api.ApiService;
import com.apache.api.entity.BaseEntity;
import com.apache.api.vo.ParamsVo;
import com.apache.api.vo.ResultEntity;
import com.apache.database.constant.SpringContextLoader;
import com.apache.method.SystemRunnable;
import com.apache.rpc.common.JsonEntityParese;
import com.apache.rpc.common.RpcUtil;
import com.apache.rpc.common.XMLMapParse;
import com.apache.tools.ConfigUtil;
import com.apache.tools.StrUtil;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * description: 启动socke端口的监听
 *
 * @author dyh 创建时间：2018年01月22日
 */
public class StartSocketPortListener implements ApplicationListener<ContextRefreshedEvent> {

    private Logger log = Logger.getLogger("clientSocket");

    private String port;

    private static boolean isStart = false;

    private StartSocketPortListener() {

    }

    public StartSocketPortListener(String port) {
        this.port = port;
    }

    public void init() {
        //init
    }

    public void registry() {
        new Thread(new ProtocolServer(port), "socket 服务线程").start();
    }

    public void setPort(String port) {
        this.port = port;
    }

    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (isStart)
            return;
        //当ApplicationContext初始化或者刷新时触发该事件
        if (event.getTimestamp() > 0) {
            registry();
        }
        isStart = true;
    }

    class ProtocolServer extends SystemRunnable {

        private ServerSocket server = null;

        private String portStr;

        public ProtocolServer(String portStr) {
            this.portStr = portStr;
        }

        public void run() {
            //是否开发socket监听，默认开启
            String openServer = StrUtil
                    .doNull(ConfigUtil.getInstance().getValueByKey("openSocketServer"), "1");
            if ("1".equals(openServer)) {
                portStr = StrUtil.doNull(portStr, "2818");
                try {
                    if (null != server) {
                        return;
                    }
                    server = new ServerSocket(Integer.parseInt(portStr));
                    isStart = true;
                } catch (Exception e1) {
                    e1.printStackTrace();
                    closeSocket(portStr);
                }
                while ("1".equals(openServer)) {
                    try {
                        Socket socket = server.accept();
                        if (null != socket) {
                            new Thread(new SocketServiceTimer(socket), "socket 服务线程").start();
                        }
                    } catch (IOException e) {
                        log.error("获取可能socke连接出错：" + e.getMessage());
                    }
                }
            }
        }

        private void closeSocket(String port) {
            try {
                if (!server.isClosed()) {
                    server.close();
                    server = null;
                    if (!StrUtil.isNull(port))
                        server = new ServerSocket(Integer.parseInt(port));
                }
            } catch (Exception e) {
                log.error("创建socket server 出错：" + e.getMessage());
            }
        }

        private class SocketServiceTimer implements Runnable {

            private Socket socket = null;

            public SocketServiceTimer(Socket socket) {
                this.socket = socket;
            }

            public void run() {
                String result = "";
                String key = "";
                if (null != socket) {
                    key = socket.getInetAddress().getHostAddress();
                }
                try {
                    String strMsg = "";
                    if (socket != null && socket.isConnected() && Thread.currentThread().isAlive()
                            && !Thread.currentThread().isInterrupted() && !socket.isClosed()) {
                        byte[] data = new byte[102400];
                        //                        InputStream in = new BufferedInputStream(socket.getInputStream());
                        //                        in.read(data);
                        InputStream inputStream = socket.getInputStream();
                        BufferedReader bufferedReader = new BufferedReader(
                                new InputStreamReader(inputStream, "UTF-8"));
                        String mg = null;
                        StringBuilder sb = new StringBuilder("");
                        while ((mg = bufferedReader.readLine()) != null) {
                            sb.append(mg);
                        }
                        strMsg = sb.toString();
                        //                        strMsg = new String(data, "UTF-8");
                        if (StrUtil.isNull(strMsg)) {
                            return;
                        }
                        ResultEntity entity = null;
                        strMsg = strMsg.trim();
                        Map<String, Object> headMap = new HashMap<String, Object>();
                        Map<String, Object> bodyMap = new HashMap<String, Object>();
                        xmlGetDataToHead(headMap, bodyMap, strMsg);
                        ///实例service接口
                        String beanId = StrUtil
                                .doNull(headMap.get("BeanId").toString(), "rpcService");
                        ApiService service = (ApiService) SpringContextLoader.getBean(beanId);
                        if (null != service) {
                            ParamsVo<BaseEntity> vo = new ParamsVo<BaseEntity>();
                            vo.setMethodKey(headMap.get("MethodCode").toString());
                            vo.setParams(bodyMap);
                            vo.setParams("requestIp", key);
                            entity = service.doService(vo);
                        } else {
                            String message =
                                    "从" + key + "请求过来的，当前工程：bean named '" + beanId + "'没有找到";
                            entity = new ResultEntity();
                            entity.setResult("false");
                            entity.setMessage(message);
                            log.info("执行socket调用出错：" + message);
                        }
                        if (!RpcUtil.isEmpty(entity)) {
                            if ("json".equalsIgnoreCase(
                                    headMap.get("ParamType").toString().toLowerCase())) {
                                result = JsonEntityParese.instance().beanToJson(entity);
                            } else {
                                result = JsonEntityParese.instance().toXml(entity);
                            }
                        }
                        //返回值长度
                        byte[] dataByteArr = result.getBytes("UTF-8");
                        int dataLen = dataByteArr.length;
                        byte[] dataByteLen = int2BytesLH(dataLen);
                        //回写返加报文
                        OutputStream os = socket.getOutputStream();
                        //发送实际数据长度
                        os.write(dataByteLen, 0, dataByteLen.length);
                        //发送实际数据内容
                        os.write(dataByteArr, 0, dataByteArr.length);
                        log.debug("dataByteArr=" + result);
                        os.flush();
                        // 关闭输出流
                        socket.shutdownOutput();
                        // 关闭资源
                        bufferedReader.close();
                        inputStream.close();
                    }
                } catch (Exception e) {
                    log.error("未能正确执行：" + e.getMessage());
                } finally {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        //
                    }
                }
            }

            private void xmlGetDataToHead(Map<String, Object> headMap, Map<String, Object> bodyMap,
                    String strMsg) {
                if (strMsg.startsWith("[{")) {//json方式传入
                    strMsg = XMLMapParse.instance().jsonToXMl(strMsg);
                }
                if (strMsg.startsWith("<?")) {//xml方式传入
                    XMLMapParse.instance().xmlGetData(strMsg, headMap, bodyMap);
                }
            }

            /**
             * description:  将int转成字节数组
             */
            private byte[] int2BytesLH(int n) {
                byte[] b = new byte[4];
                b[0] = (byte) (n & 0xff);
                b[1] = (byte) (n >> 8 & 0xff);
                b[2] = (byte) (n >> 16 & 0xff);
                b[3] = (byte) (n >> 24 & 0xff);
                return b;
            }

        }
    }

}
